表單驗證是 Web 開發中不可或缺的一部分。通過表單驗證,我們能保證用戶提交的數據正確無誤。今天我們將介紹如何使用 Vee-Validate 和 Zod 結合 TypeScript,實現表單驗證的最佳實踐。相較於傳統的驗證庫如 Yup,Zod 提供了更強大的型別推斷功能,並且與 TypeScript 的結合更加緊密。
Vee-Validate 是一個專門為 Vue 開發的表單驗證庫,與 Vue Composition API 結合緊密,並且可以很好地與 TypeScript 一起使用。Zod 是一個數據驗證和解析的庫,擁有強大的型別推斷功能,非常適合與 TypeScript 結合使用(關於 zod 相關的可以看 Day2 做初步的了解)。
首先,我們需要在項目中安裝 Vee-Validate:
bun add vee-validate @vee-validate/zod
# 如果沒看 Day2 沒安裝 zod 的記得額外輸入 bun add zod
補充:@vee-validate/zod 為 vee-validate 可以整合 zod 的一個整合擴展
在這一步中,我們將使用 Zod 來定義表單驗證的 schema,並將其應用到 Vee-Validate 中。
我們先建置一個簡單 input 元件作為表單的元件使用 (檔案: src/components/InputField.vue
)
<script setup lang="ts">
withDefaults(
defineProps<{
id: string;
type?: string;
}>(),
{
type: 'text',
}
);
const modelValue = defineModel<string>({ default: '' });
</script>
<template>
<input v-model="modelValue" :type />
</template>
顯示錯誤的元件:(檔案: src/components/ErrorMessage.vue
)
<script setup lang="ts">
withDefaults(
defineProps<{
msg?: string;
}>(),
{}
);
</script>
<template>
<span>{{ msg }}</span>
</template>
(檔案: src/components/Form.vue
)
<script lang="ts" setup>
import InputField from './InputField.vue';
import ErrorMessage from './ErrorMessage.vue'
import { useForm, useField } from 'vee-validate';
import * as zod from 'zod';
import { toTypedSchema } from "@vee-validate/zod";
// 使用 Zod 定義驗證 schema
const userSchema = zod.object({
userName: zod.string().min(3, '用戶名必須至少 3 個字符'),
email: zod.string().email('請輸入有效的電子郵件地址'),
});
const { handleSubmit, errors } = useForm({
validationSchema: toTypedSchema(userSchema),
initialValues: {
userName: '',
email: ''
}
});
const submitForm = handleSubmit((values) => {
console.log('表單數據:', values);
});
const { value: userName } = useField<string>("userName");
const { value: email } = useField<string>("email");
</script>
<template>
<form @submit.prevent="submitForm">
<div>
<label for="userName">用戶名</label>
<InputField id="userName" v-model="userName />
<ErrorMessage :msg="errors.userName" />
</div>
<div>
<label for="email">電子郵件</label>
<InputField id="email" v-model="email" type="email" />
<ErrorMessage :msg="errors.email" />
</div>
<button type="submit">提交</button>
</form>
</template>
以上元件有可能會有擴充的部分,個人建議把 typescript 的部分分成 composables
的部分,其中之一的好處,就是可以重新使用,其二點是關注點可以分開, script 和 components 可以個別來看,也可以使單一檔案的程式比較簡潔
(檔案: src/composables/useUserForm.ts
)
import { useForm, useField } from 'vee-validate';
import * as zod from 'zod';
import { toTypedSchema } from "@vee-validate/zod";
export const useUserForm = () => {
// 使用 Zod 定義驗證 schema
const userSchema = zod.object({
userName: zod.string().min(3, '用戶名必須至少 3 個字符'),
email: zod.string().email('請輸入有效的電子郵件地址'),
});
const { handleSubmit, errors } = useForm({
validationSchema: toTypedSchema(userSchema),
initialValues: {
userName: '',
email: ''
}
});
const submitForm = handleSubmit((values) => {
console.log('表單數據:', values);
});
const { value: userName } = useField<string>("userName");
const { value: email } = useField<string>("email");
return {
userName,
email,
errors,
submitForm,
};
};
export type UseUserForm = typeof useUserForm;
<script lang="ts" setup>
import { useUserForm } from '../composables/useUserForm';
const {
userName,
email,
errors,
submitForm,
} = useUserForm();
</script>
<template>
<form @submit.prevent="submitForm">
<div>
<label for="userName">用戶名</label>
<InputField id="userName" v-model="userName />
<ErrorMessage :msg="errors.userName" />
</div>
<div>
<label for="email">電子郵件</label>
<InputField id="email" v-model="email" type="email" />
<ErrorMessage :msg="errors.email" />
</div>
<button type="submit">提交</button>
</form>
</template>
在這個範例中,我們使用 Zod 定義了一個表單驗證的 schema。userName
字段需要至少 3 個字符,而 email
字段則需要符合電子郵件格式。當用戶提交表單時,Vee-Validate 將根據 Zod schema 進行驗證。
每個字段使用 Vee-Validate 提供的 useForm
useField
,自動綁定驗證邏輯。
initialValue
: 可以讓表單初始狀態有預設值,errors
: 表單如果個別欄位有錯誤訊息,所有錯誤相關的欄位可以在這裡取得。
Zod 支持靈活的自定義驗證規則,我們可以根據具體的業務需求來擴展驗證邏輯。例如,我們可以定義一個用戶名必須包含 "Vue" 的驗證規則:
const userSchema = zod.object({
userName: zod.string().min(3, '用戶名必須至少 3 個字符').regex(/Vue/, '用戶名必須包含 "Vue"'),
email: zod.string().email('請輸入有效的電子郵件地址'),
});
在這個例子中,regex
方法用於驗證用戶名中是否包含 "Vue",並且可以顯示自定義的錯誤信息。
Vee-Validate 與 TypeScript 完美結合,我們可以使用 TypeScript 的型別推斷來確保數據的正確性。當我們使用 Zod 定義驗證 schema 後,TypeScript 可以自動推斷出表單的型別,這樣我們就不需要手動定義每個表單字段的型別。
type UserSchema = z.infer<typeof userSchema>;
const initialValues: UserSchema = {
userName: 'MichaelVue',
email: 'michael@vuejs.com',
};
這樣做可以讓我們在使用表單數據時,享受 TypeScript 帶來的型別安全性與自動補全功能,減少潛在的開發錯誤。
也因為這樣我們可以把剛才的表單 typescript 寫得更嚴謹
import { useForm, useField } from 'vee-validate';
import * as zod from 'zod';
import { toTypedSchema } from "@vee-validate/zod";
// 這裡我們把 schema 和型別往外,以便型別共用
export const userSchema = zod.object({
userName: zod.string().min(3, '用戶名必須至少 3 個字符'),
email: zod.string().email('請輸入有效的電子郵件地址'),
});
export type UserSchema = zod.infer<typeof userSchema>;
export const useUserForm = () => {
// 使用 Zod 定義驗證 schema
const { handleSubmit, errors } = useForm({
validationSchema: toTypedSchema(userSchema),
initialValues: {
userName: '',
email: ''
} satisfies UserSchema // 這裡我們讓 initialValues 強制合乎規範
});
const submitForm = handleSubmit((values) => {
console.log('表單數據:', values);
});
const { value: userName } = useField<string>("userName");
const { value: email } = useField<string>("email");
return {
userName,
email,
errors,
submitForm,
};
};
export type UseUserForm = typeof useUserForm;
通過 Vee-Validate 和 Zod,我們可以實現靈活且強大的表單驗證功能。Zod 的優勢在於與 TypeScript 的深度集成,使我們在表單驗證的過程中享受靜態型別檢查與運行時驗證相結合的便利性。本文展示了如何使用 Zod 定義表單驗證規則、處理嵌套結構、實現異步驗證以及與 TypeScript 的型別推斷結合。
接下來的文章中,我們將進一步討論如何在更複雜的表單場景中應用這些驗證技術,提升表單的健壯性與用戶體驗。